package ddz.db.service;

import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 匹配池
 */
public class MatchPool {

    private static final Logger log = (Logger) LoggerFactory.getLogger(MatchPool.class);

    private final Map<Long, MatchMember> playerPool = new ConcurrentHashMap<>();
    private final int NEED_MATCH_PLAYER_COUNT;
    private final String id;
    private MatchTimeoutProcessor matchTimeoutProcessor;
    private MatchResultProcessor matchResultProcessor;

    public MatchPool(String id, int maxPlayers) {
        this.id = id;
        NEED_MATCH_PLAYER_COUNT = maxPlayers - 1;
    }

    public String getId() {
        return this.id;
    }

    public void setMatchTimeoutProcessor(MatchTimeoutProcessor matchTimeoutProcessor) {
        this.matchTimeoutProcessor = matchTimeoutProcessor;
    }

    public void setMatchResultProcessor(MatchResultProcessor matchResultProcessor) {
        this.matchResultProcessor = matchResultProcessor;
    }

    /**
     * 把玩家放入匹配池
     *
     * @param playerId
     * @param score
     * @return
     */
    public void putPlayerIntoMatchPool(long playerId, int score, Map<String, Object> params) {
        playerPool.put(playerId, new MatchMember(playerId, score, params));
    }

    private void putPlayerIntoMathPool(MatchMember player) {
        playerPool.put(player.getUid(), player);
    }

    public boolean isMatching(long playerId) {
        return playerPool.containsKey(playerId);
    }

    /**
     * 把玩家从匹配池移除
     *
     * @param playerId
     */
    public MatchMember removePlayerFromMatchPool(long playerId) {
        return playerPool.remove(playerId);
    }

    public void clear() {
        this.playerPool.clear();
    }

    public void matchProcess() {
        //先把匹配池中的玩家按分数分布
        TreeMap<Integer, Set<MatchMember>> pointMap = new TreeMap<>();
        Collection<MatchMember> poolValues = playerPool.values();
        List<MatchMember> timeoutMemebers = null;
        if (this.matchTimeoutProcessor != null) {
            timeoutMemebers = new ArrayList<>(poolValues.size());
        }
        for (MatchMember matchPlayer : poolValues) {
            if (this.matchTimeoutProcessor != null) {
                long offset = System.currentTimeMillis() - matchPlayer.getStartMatchTime();
                if (offset > this.matchTimeoutProcessor.timeout(this)) {
                    timeoutMemebers.add(matchPlayer);
                    removePlayerFromMatchPool(matchPlayer.getUid());
                    continue;
                }
            }
            Set<MatchMember> set = pointMap.get(matchPlayer.getScore());
            if (set == null) {
                set = pointMap.computeIfAbsent(matchPlayer.getScore(), k -> new HashSet<>());
            }
            set.add(matchPlayer);
        }
        if (timeoutMemebers != null && !timeoutMemebers.isEmpty()) {
            if (this.matchTimeoutProcessor != null) {
                this.matchTimeoutProcessor.doMatchTimeout(this, timeoutMemebers);
            }
        }
        for (Set<MatchMember> sameScorePlayers : pointMap.values()) {
            boolean continueMatch = true;
            while (continueMatch) {
                MatchMember oldest = null;
                for (MatchMember playerMatchPoolInfo : sameScorePlayers) {
                    oldest = playerMatchPoolInfo;
                    if (oldest != null) break;
                }
                if (oldest == null) break;
                long now = System.currentTimeMillis();
                int waitSecond = (int) ((now - oldest.getStartMatchTime()) / 1000);
                //按等待时间扩大匹配范围
                float c2 = 1.5f;
                int c3 = 5;
                int c4 = 100;
                float u = (float) Math.pow(waitSecond, c2);
                u = u + c3;
                u = (float) Math.round(u);
                u = Math.min(u, c4);
                int min = Math.max((oldest.getScore() - (int) u), 0);
                int max = oldest.getScore() + (int) u;
                int middle = oldest.getScore();
                List<MatchMember> matchedPlayerList = new ArrayList<>(NEED_MATCH_PLAYER_COUNT);
                //从中位数向两边扩大范围搜索
                for (int searchRankUp = middle, searchRankDown = middle; searchRankUp <= max || searchRankDown >= min; searchRankUp++, searchRankDown--) {
                    Set<MatchMember> thisRankPlayers = pointMap.getOrDefault(searchRankUp, new HashSet<>());
                    if (searchRankDown != searchRankUp && searchRankDown > 0) {
                        thisRankPlayers.addAll(pointMap.getOrDefault(searchRankDown, new HashSet<>()));
                    }
                    if (!thisRankPlayers.isEmpty()) {
                        if (matchedPlayerList.size() < NEED_MATCH_PLAYER_COUNT) {
                            Iterator<MatchMember> it = thisRankPlayers.iterator();
                            while (it.hasNext()) {
                                MatchMember player = it.next();
                                if (player.getUid() != oldest.getUid()) {//排除玩家本身
                                    if (matchedPlayerList.size() < NEED_MATCH_PLAYER_COUNT) {
                                        matchedPlayerList.add(player);
                                        it.remove();
                                    } else {
                                        break;
                                    }
                                }
                            }
                        } else {
                            break;
                        }
                    }
                }
                if (matchedPlayerList.size() == NEED_MATCH_PLAYER_COUNT) {
                    if (log.isDebugEnabled()) log.debug(oldest.getUid() + "|匹配到玩家数量够了|提交匹配成功处理");
                    //自己从匹配池移除
                    sameScorePlayers.remove(oldest);
                    //匹配成功处理
                    matchedPlayerList.add(oldest);
                    //从匹配池移除匹配到的玩家
                    MatchMember[] matchMembers = new MatchMember[matchedPlayerList.size()];
                    for (int i = 0; i < matchMembers.length; i++) {
                        MatchMember matchPlayer = matchedPlayerList.get(i);
                        removePlayerFromMatchPool(matchPlayer.getUid());
                        matchMembers[i] = matchPlayer;
                    }
                    if (this.matchResultProcessor != null) {
                        this.matchResultProcessor.doMatchResult(this, new MatchResult(this.id, matchMembers));
                        //System.out.println(JsonUtils.toJsonString(matchedPlayerList));
                    }
                } else {
                    //本分数段等待时间最长的玩家都匹配不到，其他更不用尝试了
                    continueMatch = false;
                    //归还取出来的玩家
                    for (MatchMember player : matchedPlayerList) {
                        Set<MatchMember> sameRankPlayer = pointMap.get(player.getScore());
                        sameRankPlayer.add(player);
                        putPlayerIntoMathPool(player);
                    }
                }
            }
        }
        if (log.isDebugEnabled()) log.debug("匹配池剩余玩家数量：" + playerPool.size());
    }

}
